﻿using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.IdentityModel.Tokens;
using Newtonsoft.Json;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Security.Cryptography;
using System.Text;

namespace App.AuthenJwt
{
    public static class Jwt
    {

        /// <summary>
        /// 生成jwt
        /// </summary>
        /// <param name="claimDtos">携带信息</param>
        /// <param name="signKey">jwt 秘钥</param>
        /// <param name="issuer">发行人</param>
        /// <param name="audience">使用人</param>
        /// <param name="expiresHour">过期时间小时</param>
        /// <returns></returns>
        public static string GenJwt(Dictionary<string, string> claimDtos, string signKey, string issuer, string audience, int expiresHour, bool isBearer = true)
        {
            var claims = new List<Claim>();
            foreach (var item in claimDtos)
            {
                claims.Add(new Claim(item.Key, item.Value));
            }

            var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(signKey));//密钥
            var token = new JwtSecurityToken(
                issuer: issuer,//颁发Token的web应用程序地址(当前程序)
                audience: audience,//token接收程序的地址(WebApi)
                claims: claims,
                notBefore: DateTime.Now,
                expires: DateTime.Now.AddHours(expiresHour),
                signingCredentials: new SigningCredentials(key, SecurityAlgorithms.HmacSha256)
            );
            var jwtToken = new JwtSecurityTokenHandler().WriteToken(token);

            if (isBearer)
            {
                return JwtBearerDefaults.AuthenticationScheme + " " + jwtToken;
            }

            return jwtToken;
        }

        /// <summary>
        /// 验证的同时直接获取负载部分PayLoad，验证成功了难得再取一次
        /// </summary>
        /// <param name="jwt"></param>
        /// <param name="validatePayLoad"></param>
        /// <returns></returns>
        public static bool ValidatePayLoad(string jwt, string signKey, string issuer, string audience, int expiresHour,
            out Dictionary<string, string> outpayLoad,
            Func<Dictionary<string, string>, bool> validatePayLoad = null)
        {
            outpayLoad = null;

            var success = true;
            var jwtArr = jwt.Split('.');
            if (jwtArr.Length < 3)//数据格式都不对直接pass
            {
                return false;
            }

            var payLoad = JsonConvert.DeserializeObject<Dictionary<string, string>>(Base64UrlEncoder.Decode(jwtArr[1]));
            //在配置文件中取出来的签名秘钥
            var hs256 = new HMACSHA256(Encoding.ASCII.GetBytes(signKey));
            //验证签名是否正确（把用户传递的签名部分取出来和服务器生成的签名匹配即可）
            success = success && string.Equals(jwtArr[2], Base64UrlEncoder.Encode(hs256.ComputeHash(Encoding.UTF8.GetBytes(string.Concat(jwtArr[0], ".", jwtArr[1])))));
            if (!success)
            {
                return success;//签名不正确直接返回
            }

            //其次验证是否在有效期内（也应该必须）
            var now = ToUnixEpochDate(DateTime.Now);
            success = success && (now >= long.Parse(payLoad["nbf"].ToString()) && now < long.Parse(payLoad["exp"].ToString()));

            //不需要自定义验证不传或者传递null即可
            if (validatePayLoad != null)
            {
                success = success && validatePayLoad(payLoad);
            }

            outpayLoad = payLoad;
            return success;
        }

        private static long ToUnixEpochDate(DateTime date) => (long)Math.Round((date.ToUniversalTime() - new DateTimeOffset(1970, 1, 1, 0, 0, 0, TimeSpan.Zero)).TotalSeconds);

        /// <summary>
        /// 获取jwt中的负载
        /// </summary>
        /// <param name="jwt"></param>
        /// <returns></returns>
        public static Dictionary<string, object> getPayLoad(string jwt)
        {
            var tokenHandler = new JwtSecurityTokenHandler();
            var securityToken = tokenHandler.ReadToken(jwt) as JwtSecurityToken;
            var stringClaimValue = securityToken.Claims;

            return null;
        }

    }
}
